22/10 Entrada y salida de datos Funciones (parámetros por default, nombrados), módulos (distintas formas de importarlos) CLASE DE LABORATORIO Vencimiento TP1 Enunciado del TP 2

Funciones

Una de las premisas de Python era que "La legibilidad cuenta", y el uso de funciones ayudan mucho en que un código sea legible.
En Python no existen los procedimientos: son todas funciones. Incluso, aunque nosotros no devolvamos ningún valor, Python lo hará por nosotros, retornando None.
La forma de devolver valores es, al igual que en C, usando la palabra reservada return y el valor a retornar. Y de igual forma, una vez que se ejecuta esa sentencia, no se ejecuta ninguna sentencia más de esa función; sin importar si está dentro de un ciclo o todavía no hayamos hecho nada.
La definición de una función comienza usando la palabra reservada def, y continúa dejando un espacio, poniendo el nombre de la función, los parámetros entre paréntesis(los paréntesis son obligatorios por más que no se pasen parámetros) y un dos puntos para terminar la línea. En las líneas que le siguen va el código de la función, que, al igual que para las estructuras de control, la forma en que se indica el bloque de código que se tiene que ejecutar es haciendo uso de la indentación.
El nombre de la función tiene que cumplir las mismas reglas para las variables, puede empezar con cualquier letra y el _ y después le puede seguir cualquier carácter alfanumérico más el _.
Por ejemplo:


In [ ]:
def sumar(x, y):  # Defino la función sumar
    return x + y

x = 4
z = 5
print sumar(x, z)  # Invoco a la función sumar con los parámetros x y z
print sumar(1, 2)  # Invoco a la función sumar con los parámetros 1 y 2

Aunque en ningún momento indicamos que lo que tiene que sumar son números, por lo que también puede sumar strings:


In [ ]:
print sumar('hola ', 'mundo')

Además, a esta función le podría agregar comentarios (docstrings) para que al hacer help de la función se entienda qué es lo que hace:


In [ ]:
def sumar(x, y):
    """Suma dos elementos y retorna el resultado.
    """
    return x + y

help(sumar)

El resultado de la función no es necesario que lo guarde en una variable, tranquilamente la puedo invocar y perder ese valor.


In [ ]:
def factorial(n):
    """Calcula el factorial de un número de forma iterativa.
    """
    for i in range(1,n):
        n *= i
    return n

fact_5 = factorial(5)  # calculo el factorial de 5 y lo guardo en fact_5
factorial(10)  # calculo el factorial de 10 y no lo guardo en ninguna variable

¿Y qué sucede si no pongo el return en una función?


In [ ]:
def imprimir(msg):
    print msg
    
imprimir('Hola mundo')

¿Y si le asigno el resultado de este procedimiento a una variable?


In [ ]:
resultado = imprimir('Hola mundo')
print resultado

Por lo que no existen los procedimientos, los "procedimientos" en realidad son funciones que devuelven None. Y una prueba más de esto es el resultado de llamar a la función type y pasarle como parámetro la función sumar y el "procedimiento" imprimir:


In [ ]:
print type(imprimir)
print type(sumar)
print sumar

Ahora, si la función es un tipo de dato, significa que se lo puedo asignar a una variable...


In [ ]:
mi_suma = sumar

¿Y qué pasa si ahora llamo a mi_suma con los parámetros 1 y 2 como hice antes con sumar?


In [ ]:
print mi_suma(1, 2)

Lista de parámetros

¿Qué pasa cuando no sabemos cuántos parámetros nos pueden pasar, pero si sabemos qué hacer con ellos?


In [ ]:
def sumar(*args):
    suma = 0
    for e in args:
        suma += e
    return suma

print sumar(1, 2)
print sumar(1, 2, 3, 4, 5)
print sumar(*[1, 2, 3, 4, 5, 6])
print sumar

Parámetros por defecto

Algo más común que no saber la cantidad de parámetros que nos van a pasar es asumir que ciertos parámetros pueden no pasarlos y para ellos asumiremos un valor por defecto.
Por ejemplo:


In [ ]:
def imprimir_parametros(param1, param2, param3=5, param4="es el cuarto parametro", param5=False):
    print param1, param2, param3, param4, param5

Para esta función nos pueden pasar 2, 3, 4 o 5 parámetros. Si nos pasan los 5 parámetros, se imprimirán los valores que nos pasen:


In [ ]:
imprimir_parametros(1, 2, 3, 4, 5)

Ahora, si nos pasan 4 parámetros, el intérprete asumirá que el faltante es param5, por lo que dicho parámetro tomará el valor False. Y lo mismo pasa con el resto de los parámetros.


In [ ]:
imprimir_parametros(1, 2, 3, 4)
imprimir_parametros(1, 2, 3)
imprimir_parametros(1, 2)

¿Y si le pasamos un sólo parámetro?.


In [ ]:
imprimir_parametros(1)

¿Y qué pasa si quiero pasarle los parámetros 1, 2 y el 5?.
No es problema, para eso tenemos que usar parámetros nombrados:


In [ ]:
imprimir_parametros(1, 2, param5="Este el parametro5")

Lo mismo pasa si lo que quiero cambiar es el cuatro parámetro:


In [ ]:
imprimir_parametros(1, 2, param4=4)

Hasta se pueden nombrar todos los parámetros:


In [ ]:
imprimir_parametros(param5=1, param3=2, param1=3, param2=4, param4=5)

Si bien puede parecer innecesario el uso de parámetros nombrados, en algunas oportunidades se suele usar para agregar claridad y legibilidad al código, y en otros para pasarle un diccionario:


In [ ]:
parametros = {
    'param1': 1,
    'param2': 2,
    'param3': 3,
    'param4': 4,
    'param5': 5,
}

imprimir_parametros(**parametros)

Uso de módulos externos

Así como en Pascal usando la cláusula Uses podíamos usar código que no pertenecía al archivo que estábamos codificando, en Python podemos hacer lo mismo usando la cláusula import y poniendo a continuación el nombre del módulo.
Por ejemplo, si queremos importar el módulo datetime para trabajar con fechas y horas, tendríamos que hacer:

import datetime

Para usarlo simplemente tenemos que poner el nombre del módulo, un punto y la función que queramos usar.
En este caso, dentro del módulo datetime vamos a usar la función que se encuentra en date y se llama today().


In [ ]:
import datetime

print datetime.date.today()

Pero a diferencia de Pascal y C, acá podemos elegir importar una función o algo en particular de ese módulo, en lugar de traerlo todo. Para eso tendríamos que poner en primer lugar la cláusula from, luego el nombre del módulo y a continuación la cláusula import todo lo que queremos importar separada por comas.
Por ejemplo, del módulo datetime podríamos traer los submódulos date y time. Después, para usarlos simplemente lo hacemos llamando lo que importamos sin el nombre del módulo.


In [ ]:
from datetime import date, time

print date.today()
print time(1, 23, 32)

Si tenemos un archivo llamado ejemplo.py que tiene el siguiente código:

def imprimir(param):
    print param

def sumar(n1, n2):
    return n1+n2

y queremos importarlo a otro archivo y usarlo, podemos hacer:

import ejemplo

ejemplo.imprimir("123")
print ejemplo.sumar(2,3)

Y, como dijimos, también podemos importar solo una función de ese módulo y usarla como si estuviera definida dentro de nuestro contexto.

from ejemplo import sumar

print sumar(4, 5)

Ejercicios

  1. Escribir un programa que le pregunte un número al usuario. Si el número es 5, que muestre "Suerte!"; si el número es mayor a 10, que muestre "Grande!"; para los otros casos que muestre "Sin suerte, :(".
  2. Crear una función que calcule el factorial de un número, pedirle al usuario que ingrese un número y mostrarle el factorial de dicho número.
  3. Crear una función que indique si un número es primo o no e imprimir todos los números primos entre 1 y un número que se le solicite al usuario.
  4. Hacer un programa que genere un número entero al azar (módulo random) entre 0 y 1000, y le vaya pidiendo al usuario que ingrese números enteros para adivinarlo. Si el usuario ingresa un número menor que el objetivo, muestra "Es más alto!"; si el usuario ingresa uno mayor, muestra "Es más bajo!"; si el usuario acierta, muestra "Ganaste!!!", y termina. Si el usuario no acertó en 7 intentos, que muestre "Perdiste! :(" y termine.
  5. Armar una función que reciba una tupla y devuelva si la tupla está ordenada (de menor a mayor) o no.

In [ ]: